//
//  ZYPBCore.m
//  ZYWebImage
//
//  Created by wangzhipan on 2025/4/25.
//

#import "ZYPBCore.h"
#import "ISocketProtocol.h"
#import "ZYSocketManager.h"
#import "ZYPBHeaderDataUitl.h"
#import "ZYPBToken.h"
#import "ZYPBOperationIDMgr.h"

static NSString * const kPBErrorDomain = @"com.ZYPBCore.www";
@interface ZYPBCore() <ISocketDelegate>
@property (nonatomic, strong) id<ISocketProtocol> socketMgr;
@property (nonatomic, strong, nullable) NSMutableDictionary <NSString *, ZYPBToken *>*pbMap;
@property (nonatomic, strong, nullable) NSMutableDictionary <NSString *, ZYPBToken *>*clsMap;
@property (nonatomic, strong, nullable) NSMutableDictionary <NSString *, NSMutableArray<ZYPBNotificationToken *>*>*pbNotificationMap;
@property (nonatomic, strong, nullable) NSMutableDictionary <NSNumber *, ZYPBCompletedBlock>*completedBlockMap;
@property (nonatomic, strong, nullable) dispatch_queue_t pbQueue;
@property (nonatomic, strong, nullable) NSMutableData *receivebuffer;
@property (nonatomic, strong, nullable) dispatch_queue_t dataParseQueue;
@property (nonatomic, strong, nullable) ZYPBOperationIDMgr *idMgr;
@end

@implementation ZYPBCore
+ (instancetype)sharedCore
{
    static id _instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}

- (instancetype)init
{
    if (self = [super init]) {
        [self p_initSocketManager];
        self.pbMap = [NSMutableDictionary dictionary];
        self.clsMap = [NSMutableDictionary dictionary];
        self.pbNotificationMap = [NSMutableDictionary dictionary];
        self.pbQueue = dispatch_queue_create("com.ZYPBCore_pbQueue.www", DISPATCH_QUEUE_SERIAL);
        self.idMgr = [[ZYPBOperationIDMgr alloc] init];
        self.completedBlockMap = [NSMutableDictionary dictionary];
        self.receivebuffer = [NSMutableData data];
        self.dataParseQueue = dispatch_queue_create("com.ZYPBCore_dataParseQueue.www", DISPATCH_QUEUE_SERIAL);;
    }
    return self;
}

- (void)startConnect
{
    NSError *error = nil;
    [self.socketMgr connect:&error];
    if (error) {
        NSLog(@"%s:: connect error == %@", __func__, error);
    }
    else {
        [self p_startHeartbeat];
    }
}

- (void)disconnect
{
    [self.socketMgr disconnect];
    [self p_stopHeartbeat];
}

- (void)clearAllData
{
    dispatch_sync(_pbQueue, ^{
        [self.pbMap removeAllObjects];
        [self.clsMap removeAllObjects];
        [self.pbNotificationMap removeAllObjects];
        [self.completedBlockMap removeAllObjects];
    });
    
    dispatch_sync(_dataParseQueue, ^{
        [self.receivebuffer setLength:0];
    });
}

- (void)clearAllDataAsync
{
    dispatch_async(_pbQueue, ^{
        [self.pbMap removeAllObjects];
        [self.clsMap removeAllObjects];
        [self.pbNotificationMap removeAllObjects];
        [self.completedBlockMap removeAllObjects];
    });
    
    dispatch_async(_dataParseQueue, ^{
        [self.receivebuffer setLength:0];
    });
}

- (void)registerPBForMax:(uint32_t)maxType
                     min:(uint32_t)minType
            requestClass:(Class)requestClass
           responseClass:(Class)responseClass
{
    NSString *key = PBKeyForType(maxType, minType);
    dispatch_async(_pbQueue, ^{
        ZYPBToken *token = self.pbMap[key];
        if (!token) {
            token = [[ZYPBToken alloc] initWithMax:maxType minType:minType reqClass:requestClass rspClass:responseClass pbCore:self];
            self.pbMap[key] = token;
            self.clsMap[NSStringFromClass(requestClass)] = token;
        }
    });
}

- (id<IPBToken>)registerPBNotificationForMax:(uint32_t)maxType
                                         min:(uint32_t)minType
                               responseClass:(Class)responseClass
                              completedBlock:(ZYPBCompletedBlock)completedBlock;
{
    __block ZYPBNotificationToken *token = nil;
    dispatch_sync(_pbQueue, ^{
        token = [[ZYPBNotificationToken alloc] initWithMax:maxType minType:minType rspClass:responseClass pbCore:self];
        token.completedBlock = completedBlock;
        NSString *key = PBKeyForType(maxType, minType);
        NSMutableArray *tokens = self.pbNotificationMap[key];
        if (!tokens) {
            tokens = [NSMutableArray arrayWithCapacity:2];
        }
        [tokens addObject:token];
        self.pbNotificationMap[key] = tokens;
    });
    return token;
}

- (void)removePBToken:(id<IPBToken>)token ForKey:(NSString *)key;
{
    dispatch_async(_pbQueue, ^{
        NSMutableArray *tokens = self.pbNotificationMap[key];
        if (tokens.count) {
            [tokens removeObject:token];
            return;
        }
        
        if (self.pbMap[key]) {
            [self.pbMap removeObjectForKey:key];
        }
    });
}

- (void)sendPBRequest:(GPBMessage *)request
       completedBlock:(ZYPBCompletedBlock)completedBlock
{
    __block ZYPBToken *token = nil;
    dispatch_sync(_pbQueue, ^{
        token = self.clsMap[NSStringFromClass(request.class)];
    });
    if (!token) {
        NSLog(@"%s:: sendPBRequest 【%@】 unregister", __func__, request);
        if (completedBlock) {
            NSError *error = [NSError errorWithDomain:kPBErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey:@"request unregister"}];
            completedBlock(nil, error);
        }
        return;
    }
    
    NSData *payload = [request data];
    uint32_t opId = [self.idMgr nextID];
    NSData *sendData = [ZYPBHeaderDataUitl configHeaderForData:payload max:token.maxType min:token.minType operationId:opId];
    
    [self.socketMgr asyncSendData:sendData];
    
    dispatch_async(_pbQueue, ^{
        self.completedBlockMap[@(opId)] = completedBlock;
    });
}

#pragma mark - private
- (void)p_initSocketManager
{
    ZYSocketManager *socketMgr = [[ZYSocketManager alloc] initWithHost:@"default" port:10100];
    socketMgr.delegate = self;
    //信令心跳是10s一次，超时时间一般设置成2~3倍心跳时间
    socketMgr.timeout = 25;
    socketMgr.autoReconnect = YES;
    socketMgr.reconnectDelay = 2;
    socketMgr.maxReconnectAttempts = 8;
    self.socketMgr = socketMgr;
}

- (void)p_startHeartbeat
{
    //信令心跳是10s一次
    [self.socketMgr startHeartbeatWithData:[ZYPBHeaderDataUitl heartbeatData] interval:10];
}

- (void)p_stopHeartbeat
{
    [self.socketMgr stopHeartbeat];
}

- (void)p_processReceiveData
{
    while (self.receivebuffer.length > sizeof(PBDataHeader)) {
        PBDataHeader header;
        [self.receivebuffer getBytes:&header length:sizeof(PBDataHeader)];
        
        //检查下receiveData是否大于等于一个消息长度
        if (self.receivebuffer.length >= header.length + sizeof(PBDataHeader)) {
            NSData *payload = [self.receivebuffer subdataWithRange:NSMakeRange(sizeof(PBDataHeader), header.length)];
            //先从缓冲区移除这一段data
            [self.receivebuffer replaceBytesInRange:NSMakeRange(0, header.length + sizeof(PBDataHeader))
                                          withBytes:NULL
                                             length:0];
            
            //异步解析消息
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                [self p_parseMessageForHeader:header payload:payload];
            });
        }
        else {
            break;  //继续接收data
        }
    }
}

- (void)p_parseMessageForHeader:(PBDataHeader)header payload:(NSData *)payload
{
    //首先获取对应的cls
    Class cls = nil;
    
    //检查是否pb请求的响应
    __block ZYPBToken *token = nil;
    __block NSArray <ZYPBNotificationToken *>*notificationTokens = nil;
    dispatch_sync(_pbQueue, ^{
        token = self.pbMap[PBKeyForType(header.maxType, header.minType - 1)];
        if (token && [token isRespondToMaxType:header.maxType minType:header.minType]) {
            return;
        }
        token = nil;
        notificationTokens = [self.pbNotificationMap[PBKeyForType(header.maxType, header.minType)] copy];
    });
    
    GPBMessage *message = nil;
    if (token) {
        cls = token.rspCls;
    }
    else if (notificationTokens.count) {
        cls = notificationTokens.firstObject.rspCls;
    }
    
    if (cls && [cls respondsToSelector:@selector(parseFromData:error:)]) {
        message = [cls performSelector:@selector(parseFromData:error:) withObject:payload withObject:NULL];
    }
    
    NSError *error = nil;
    if (!cls || !message) {
        NSString *reason = [NSString stringWithFormat:@"ZYPBCore max: %u min: %u error, not parse, because register cls is %@, PBMessage is %@", header.maxType, header.minType, cls, message];
        error = [NSError errorWithDomain:kPBErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: reason}];
        NSLog(@"%s, error reason: %@", __func__, reason);
        return;
    }
    //解析成功，回调消息
    if (token) {
        //先获取operationId，根据operationId获取对应的completedBlock
        uint32_t opId = header.operationId;
        __block ZYPBCompletedBlock completedBlock = nil;
        dispatch_sync(_pbQueue, ^{
            completedBlock = self.completedBlockMap[@(opId)];
            //pb请求，响应完移除对应的block
            [self.completedBlockMap removeObjectForKey:@(opId)];
        });
        dispatch_async(dispatch_get_main_queue(), ^{
            completedBlock ? completedBlock(message, nil) : nil;
        });
    }
    else if(notificationTokens.count){
        //pb 广播，需要都响应，并且可以响应多次
        dispatch_async(dispatch_get_main_queue(), ^{
            for (ZYPBNotificationToken *notifyToken in notificationTokens) {
                [notifyToken doCompletedOfresponseObj:message error:nil];
            }
        });
    }
}

#pragma mark - ISocketDelegate
- (void)socketDidConnect:(nullable id <ISocketProtocol>)socketManager
{
    NSLog(@"%s:: socketDidConnect", __func__);
}

- (void)socket:(nullable id <ISocketProtocol>)socket didDisconnectWithError:(nullable NSError *)error
{
    NSLog(@"%s:: %@", __func__, error);
    dispatch_async(self.dataParseQueue, ^{
        [self.receivebuffer setLength:0];
    });
}
- (void)socket:(nullable id <ISocketProtocol>)socket didReceiveData:(nullable NSData *)data
{
    dispatch_async(self.dataParseQueue, ^{
        [self.receivebuffer appendData:data];
        [self p_processReceiveData];
    });
}
- (void)socket:(nullable id <ISocketProtocol>)socket didSendData:(nullable NSData *)data
{
    NSLog(@"%s", __func__);
}
- (void)socket:(nullable id <ISocketProtocol>)socket didFailError:(nullable NSError *)error
{
    //发送失败，抛出给业务决定是否重新发送
    if (error.code == ISocketErrorCodeSendFaild) {
        NSData *data = error.userInfo[kSocketOriginDataKey];
        if (data) {
            uint32_t opId = 0;
            [data getBytes:&opId range:NSMakeRange(8, 4)];
            
            if (opId > 0) {
                dispatch_async(_pbQueue, ^{
                    ZYPBCompletedBlock block = self.completedBlockMap[@(opId)];
                    if (block) {
                        dispatch_async(dispatch_get_main_queue(), ^{
                            block(nil, error);
                        });
                        [self.completedBlockMap removeObjectForKey:@(opId)];
                    }
                });
            }
        }
    }
    else if (error.code == ISocketErrorCodeReceiveFild ||
             error.code == ISocketErrorCodeConnectClosed) {
        
        dispatch_async(_dataParseQueue, ^{
            [self.receivebuffer setLength:0];
        });
    }
}
@end
